package refactorplugin.views;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.Observer;

import model.CodeRefactoringModel;

import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.*;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.*;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.SWT;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;

import presenters.Presenter;
import refactor.Activator;
import views.TaskInfoLabelProvider;
import views.ViewContentProvider;
import entity.TaskInfo;
import events.EventGenerator;

import org.eclipse.ui.*;

/**
 * This sample class demonstrates how to plug-in a new
 * workbench view. The view shows data obtained from the
 * model. The sample creates a dummy model on the fly,
 * but a real implementation would connect to the model
 * available either in this or another plug-in (e.g. the workspace).
 * The view is connected to the model using a content provider.
 * <p>
 * The view uses a label provider to define how model
 * objects should be presented in the view. Each
 * view can present the same model objects using
 * different labels and icons, if needed. Alternatively,
 * a single label provider can be shared between views
 * in order to ensure that objects of the same type are
 * presented in the same way everywhere.
 * <p>
 */

public class RefactorView extends ViewPart implements Observer {

	/**
	 * The ID of the view as specified by the extension.
	 */
	public static final String ID = "refactorplugin.views.RefactorView";

	private TreeViewer _viewer;
	private TableViewer _errorViewer;
	private DrillDownAdapter _drillDownAdapter;
/*	private Action action1;
	private Action action2;
	private Action doubleClickAction;*/

	private CodeRefactoringModel _model;

	private EventGenerator _eventGenerator;

	private Presenter _presenter;

	private INotificationObject _notificationObject;

	private IAction _performAction;

	private IAction _undoAction;

	private IAction _setParamsAction;

	//private ICellModifier _cellModifier;

	/*
	 * The content provider class is responsible for
	 * providing objects to the view. It can wrap
	 * existing objects in adapters or simply return
	 * objects as-is. These objects may be sensitive
	 * to the current input of the view, or ignore
	 * it and always show the same content 
	 * (like Task List, for example).
	 */	

	/**
	 * The constructor.
	 */
	public RefactorView() {
		_model = new CodeRefactoringModel();
		_model.addObserver(this);
		_model.updateNotify();
		_eventGenerator = new EventGenerator();
		_presenter  = new Presenter(_model);
		_eventGenerator.addObserver(_presenter);
	}
	
	private void editTaskInfoForm(TaskInfo serializedMap) {
		Display display =_viewer.getControl().getDisplay();
		final Shell shell = new Shell(display);//_viewer.getControl().getShell();
		shell.setText("Set task parameters");
		URL installURL = Activator.getDefault().getDescriptor().getInstallURL();
		try {
			ImageDescriptor iD = ImageDescriptor.createFromURL(new URL(installURL, "./icons/params.png"));
			shell.setImage(iD.createImage());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			shell.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT));
		}
		
		final TaskInfo taskInfo = serializedMap;
		int x=0,y=0;
		final Map<String, Text> textFields = new HashMap<String, Text>();
		int maxLabelWidth = 0;
		for (String key : serializedMap.getParameters().keySet()) {
			Label currentKey = new Label(shell,SWT.WRAP);
			currentKey.setText(key);
			currentKey.setLocation(x, y);
			currentKey.pack();
			if (maxLabelWidth<currentKey.getBounds().width)
				maxLabelWidth = currentKey.getBounds().width;  
			Text currentValue = new Text(shell, SWT.BORDER);//SWT.SINGLE);//.WRAP);//.NONE);
			currentValue.setText(serializedMap.getParameters().get(key));
			currentValue.setLocation(0, y);
			currentValue.pack();
			textFields.put(key,currentValue);
			y+=23;
		}
		for (Entry<String, Text> entry: textFields.entrySet())
			entry.getValue().setLocation(maxLabelWidth+5,entry.getValue().getLocation().y);
		final Button save = new Button(shell,SWT.SAVE);
		final Button saveAndPerform = new Button(shell,SWT.SAVE);
		final Label validationError = new Label(shell,SWT.WRAP);
		/*org.eclipse.swt.graphics.*/Color c = new /*org.eclipse.swt.graphics.*/Color(display, 200, 28, 28);
		
		//final Button save = new Button(shell,SWT.SAVE);
		save.setText("Save parameters");
		save.setLocation(0/*shell.getSize().x/2-save.getSize().x/2*/, y);
		save.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				for (Entry<String, Text> entry: textFields.entrySet()) {
					String identifier = entry.getValue().getText();
					IStatus validateClassFileName = JavaConventions.validateIdentifier(identifier);
					if (!validateClassFileName.isOK()) {//.getCode()!=IStatus.OK) {
						validationError.setText("Wrong name:"+identifier);
						validationError.setVisible(true);
						//save.setLocation(0, validationError.getLocation().y+15);
						//shell.setBounds(shell.getBounds().x, shell.getBounds().y, shell.getBounds().width, save.getLocation().y+55);
						return;
					}
				}
				for (String key : taskInfo.getParameters().keySet())
					taskInfo.getParameters().put(key, textFields.get(key).getText());
				System.out.println("taskInfo "+taskInfo);
				shell.close();
				_viewer.refresh();
			}
			
			@Override
			public void widgetDefaultSelected(SelectionEvent arg0) {
			}
		});
		save.pack();
		//final Button saveAndPerform = new Button(shell,SWT.SAVE);
		saveAndPerform.setText("Save and Perform");
		saveAndPerform.setLocation(save.getSize().x+5, y);
		saveAndPerform.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				for (Entry<String, Text> entry: textFields.entrySet()) {
					String identifier = entry.getValue().getText();
					IStatus validateClassFileName = JavaConventions.validateIdentifier(identifier);
					if (!validateClassFileName.isOK()) {//.getCode()!=IStatus.OK) {
						validationError.setText("Wrong name:"+identifier);
						validationError.setVisible(true);
						//save.setLocation(0, validationError.getLocation().y+15);
						//shell.setBounds(shell.getBounds().x, shell.getBounds().y, shell.getBounds().width, save.getLocation().y+55);
						return;
					}
				}
				for (String key : taskInfo.getParameters().keySet())
					taskInfo.getParameters().put(key, textFields.get(key).getText());
				
				LinkedList<TaskInfo> linkedList = new LinkedList<TaskInfo>();
				linkedList.add(taskInfo);
				_eventGenerator.generate(linkedList, "perform");
				shell.close();
				_viewer.refresh();
			}
			
			@Override
			public void widgetDefaultSelected(SelectionEvent arg0) {
			}
		});
		saveAndPerform.pack();
		validationError.setForeground(c);
		validationError.setText("Wrong identifier name");
		validationError.setVisible(false/*true*/);
		validationError.pack();
		validationError.setLocation(0/*shell.getSize().x/2-save.getSize().x/2*/, y+23);
		shell.pack();
		shell.open ();
	}
	
	class ExceptionProvider implements ITableLabelProvider {
		
		@Override
		public void removeListener(ILabelProviderListener arg0) {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public boolean isLabelProperty(Object arg0, String arg1) {
			// TODO Auto-generated method stub
			return false;
		}
		
		@Override
		public void dispose() {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public void addListener(ILabelProviderListener arg0) {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public String getColumnText(Object element, int columnIndex) {
			Exception e = (Exception) element;
		    switch (columnIndex) {
		    case 0:
		    	return e.getMessage();
		    default:
		    	return "";
		    }
		}
		
		@Override
		public Image getColumnImage(Object arg0, int arg1) {
			// TODO Auto-generated method stub
			return null;
		}
	};
	
	/**
	 * This is a callback that will allow us
	 * to create the viewer and initialize it.
	 */
	public void createPartControl(Composite parent) {
		_viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.SMOOTH | SWT.RESIZE | SWT.Resize);
		_drillDownAdapter = new DrillDownAdapter(_viewer);
		_viewer.setContentProvider(new TreeViewContentProvider());
		_viewer.setLabelProvider(new TreeViewLabelProvider());
		_viewer.setInput(getViewSite());
		if (_notificationObject!=null)
			_notificationObject.show(_viewer);
		_viewer.expandAll();
		_viewer.getControl().setSize((int) (parent.getSize().x*0.75), parent.getSize().y);
		
	
		_errorViewer = new TableViewer(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.SMOOTH | SWT.RESIZE | SWT.Resize);
		_errorViewer.getControl().setSize((int) (parent.getSize().x*0.25), parent.getSize().y);
		
		final Table table = _errorViewer.getTable();
		
		TableColumn errorDescriptionColumn = new TableColumn(table, SWT.LEFT);
		errorDescriptionColumn.setText("Code review events");
		errorDescriptionColumn.setWidth(700);
		
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		
		_errorViewer.setContentProvider(new ArrayContentProvider());// ViewContentProvider());
		_errorViewer.setLabelProvider(new ExceptionProvider());
		
	
		// Create the help context id for the viewer's control
		PlatformUI.getWorkbench().getHelpSystem().setHelp(_viewer.getControl(), "RefactorPlugin.viewer");
		makeActions();
		hookContextMenu();
		/*hookDoubleClickAction();*/
		contributeToActionBars();
	}
	
	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				RefactorView.this.fillContextMenu(manager, _viewer.getSelection());
			}
		});
		Menu menu = menuMgr.createContextMenu(_viewer.getControl());
		_viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, _viewer);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(_performAction);
		manager.add(_undoAction);
		manager.add(_setParamsAction);
	}

	private void fillContextMenu(IMenuManager manager, ISelection iSelection) {
		if (!(iSelection instanceof IStructuredSelection))
			return;
		IStructuredSelection selection = (IStructuredSelection)_viewer.getSelection();
		Object selectedObject = selection.getFirstElement();
		if (!(selectedObject instanceof TreeLeaf))
			return;
		TaskInfo taskInfo = (TaskInfo)((TreeLeaf)selection.getFirstElement()).getElement();
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
		if (taskInfo.isActive()) {
			manager.add(_performAction);
			if (taskInfo.getParameters().keySet().size()>0)
				manager.add(_setParamsAction);
		} else
			if (taskInfo.isLastPerformed())
				manager.add(_undoAction);
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}
	
	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(_performAction);
		manager.add(_undoAction);
		manager.add(_setParamsAction);
		//manager.add(action2);
	}
	
	private void generateProducerEvent(IStructuredSelection selection, String action) {
		if (((TreeComponent)selection.getFirstElement()).isLeaf()) {
			LinkedList<TaskInfo> linkedList = new LinkedList<TaskInfo>();
			linkedList.add((TaskInfo)((TreeLeaf)selection.getFirstElement()).getElement());
			_eventGenerator.generate(linkedList, action);//"perform");
		}
	}
	
	private void makePerform() {
		_performAction = new Action() {
			public void run() {
				IStructuredSelection selection = (IStructuredSelection)_viewer.getSelection();
				IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry();
				if (((TreeComponent)selection.getFirstElement()).isLeaf()) {
					TaskInfo taskInfo =(TaskInfo)((TreeLeaf)selection.getFirstElement()).getElement();
					IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(taskInfo.getPath());
					IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
					String editorID = editorRegistry.getDefaultEditor(file.getFullPath().toString()).getId();
				    try {
						page.openEditor(new FileEditorInput(file), editorID, true, IWorkbenchPage.MATCH_INPUT);
					} catch (PartInitException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				generateProducerEvent(selection, "perform");
			}
		};
		_performAction.setText("Perform");
		_performAction.setToolTipText("Perform task work");
		_performAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
			getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
	}
	
	private void makeUndo() {
		_undoAction = new Action() {
			public void run() {
				IStructuredSelection selection = (IStructuredSelection)_viewer.getSelection();
				generateProducerEvent(selection, "undo");
			}
		};
		_undoAction.setText("Undo");
		_undoAction.setToolTipText("Undo task work");
		_undoAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
			getImageDescriptor(ISharedImages.IMG_TOOL_UNDO));
	}
	
	private void makeSetParams() {
		_setParamsAction = new Action() {
			public void run() {
				IStructuredSelection selection = (IStructuredSelection)_viewer.getSelection();
				editTaskInfoForm((TaskInfo)((TreeLeaf)selection.getFirstElement()).getElement());
			}
		};
		_setParamsAction.setText("Set params & Perform...");
		_setParamsAction.setToolTipText("Set params & Perform...");
		_setParamsAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
			getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
	}

	private void makeActions() {
		makePerform();
		makeUndo();
		makeSetParams();		
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		_viewer.getControl().setFocus();
	}
	
	
	/**
	 * SampleView is a View from the architecture pattern Model-View-Presenter.
	 * Is observes the events happened on the Model.
	 */
	@Override
	public void update(Observable arg0, Object notificationObject) {
		System.out.println("NOTIF OBJ "+notificationObject);
		_notificationObject = (INotificationObject)notificationObject;
		if (_notificationObject.isTable())
			_notificationObject.show(_errorViewer);
		else
			_notificationObject.show(_viewer);
	}
}